package jmine.selenium.services.validar;

import java.util.Iterator;
import java.util.List;

import jmine.selenium.services.factory.SeleniumServiceFactory;
import jmine.tec.di.annotation.Injected;
import jmine.tec.services.annotations.Execution;
import jmine.tec.services.annotations.Input;
import jmine.tec.services.annotations.ServiceImplementor;
import jmine.tec.services.api.ActionsEnum;
import jmine.tec.services.api.Service;
import jmine.tec.services.api.ServiceExecutionException;
import jmine.tec.services.api.ServiceProperties;
import jmine.tec.services.api.ServiceValidationExecutionException;
import jmine.tec.services.api.ServicesAPIMessages;
import jmine.tec.services.api.factory.ServiceCreationException;
import jmine.tec.services.impl.ServicesImplMessages;
import bancosys.tec.services.api.ServiceAssert;

/**
 * Servico para validacao de cenários negativos.
 * 
 * @author lundberg
 * @author rodrigok
 */
@ServiceImplementor(action = ActionsEnum.VALIDAR)
public class ErroService {
    private static final String ACTION = "Action";

    private static final String SERVICE = "Service";

    private static final String MESSAGE = "Mensagem";

    // injected
    private SeleniumServiceFactory serviceFactory;

    // obrigatorios
    private ActionsEnum action;

    private ServiceProperties properties;

    /**
     * Executa o servico e valida que a mensagem de erro adequada foi gerada.
     * 
     * @throws ServiceExecutionException caso ocorra algo errado.
     */
    @Execution
    public void execute() throws ServiceExecutionException {
        Service<?> service;
        try {
            service = this.serviceFactory.getService(this.action, this.properties.getRaw(SERVICE), this.properties);
        } catch (ServiceCreationException e) {
            throw new ServiceExecutionException(ServicesAPIMessages.FACTORY_INSTANTIATION_ERROR.create(), e);
        }
        try {
            this.validarServico(service);
            service.execute();
            ServiceAssert.fail(ServicesImplMessages.ASSERT_FAILED_ERROR_EXPECTED.create());
        } catch (Throwable e) {
            this.validateException(e.getMessage());
        }
    }

    /**
     * Agrupa as mensagens de erro obtidas na validacao de servicos.
     * 
     * @param service {@link Service}
     * @throws Exception Exception
     */
    private void validarServico(Service<?> service) throws Exception {
        List<String> errors = service.validate();
        if (!errors.isEmpty()) {
            throw new ServiceValidationExecutionException(ServicesAPIMessages.VALIDATION_VALIDATION_ERROR.create(this
                    .getMessageError(errors)));
        }
    }

    /**
     * Extrai a mensagem de errado pega em validacoes de servicos.
     * 
     * @param errors Lista de erros encontrados.
     * @return Mensagem concatenada com todos os erros encontrados.
     */
    private String getMessageError(List<String> errors) {
        Iterator<String> errorsIt = errors.iterator();
        StringBuilder sb = new StringBuilder(errorsIt.next());
        while (errorsIt.hasNext()) {
            sb.append("\n").append(errorsIt.next());
        }
        return sb.toString();
    }

    /**
     * Valida a mensagem da exception, caso uma mensagem tenha sido definida
     * 
     * @param exceptionMessage e
     */
    private void validateException(String exceptionMessage) {
        if (this.properties.containsKey(MESSAGE)) {
            ServiceAssert.assertEquals(this.properties.getRaw(MESSAGE), exceptionMessage);
        }
    }

    /**
     * @param serviceFactory the serviceFactory to set
     */
    @Injected
    public void setServiceFactory(SeleniumServiceFactory serviceFactory) {
        this.serviceFactory = serviceFactory;
    }

    /**
     * @param action the action to set
     */
    @Input(fieldName = ACTION)
    public void setAction(ActionsEnum action) {
        this.action = action;
    }

    /**
     * @param message the message to set
     */
    @Input(fieldName = MESSAGE, required = false)
    public void setMessage(String message) {
        // OK, deixado apenas para que seja gerada documentacao
    }

    /**
     * @param service the service to set
     */
    @Input(fieldName = SERVICE)
    public void setService(String service) {
        // OK, deixado apenas para que seja gerada documentacao
    }

    /**
     * @param properties the properties to set
     */
    @Injected
    public void setProperties(ServiceProperties properties) {
        this.properties = properties;
    }
}
